/*
 * Copyright 2019 Google LLC
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#ifndef SkImageFilter_Base_DEFINED
#define SkImageFilter_Base_DEFINED

#include "include/core/SkColorSpace.h"
#include "include/core/SkImageFilter.h"
#include "include/core/SkImageInfo.h"
#include "include/private/base/SkTArray.h"
#include "include/private/base/SkTemplates.h"

#include "src/core/SkImageFilterTypes.h"

#include <optional>

// True base class that all SkImageFilter implementations need to extend from. This provides the
// actual API surface that Skia will use to compute the filtered images.
class SkImageFilter_Base : public SkImageFilter {
public:
    /* *
     * Request a new filtered image to be created from the src image. The returned skif::Image
     * provides both the pixel data and the origin point that it should be drawn at, relative to
     * the layer space defined by the provided context.
     *
     * If the result image cannot be created, or the result would be transparent black, returns
     * a skif::Image that has a null special image, in which its origin should be ignored.
     *
     * TODO: Right now the imagefilters sometimes return empty result bitmaps/
     * specialimages. That doesn't seem quite right.
     */
    skif::FilterResult filterImage(const skif::Context &context) const;

    /* *
     * Create a filtered version of the 'src' image using this filter. This is basically a wrapper
     * around filterImage that prepares the skif::Context to filter the 'src' image directly,
     * for implementing the SkImages::MakeWithFilter API calls.
     */
    sk_sp<SkImage> makeImageWithFilter(sk_sp<skif::Backend> backend, sk_sp<SkImage> src, const SkIRect &subset,
        const SkIRect &clipBounds, SkIRect *outSubset, SkIPoint *offset) const;

    /* *
     *  Calculate the smallest-possible required layer bounds that would provide sufficient
     *  information to correctly compute the image filter for every pixel in the desired output
     *  bounds. The 'desiredOutput' is intended to represent either the root render target bounds,
     *  or the device-space bounds of the current clip. If the bounds of the content that will be
     *  drawn into the layer is known, 'knownContentBounds' should be provided, since it can be
     *  used to restrict the size of the layer if the image filter DAG does not affect transparent
     *  black.
     *
     *  The returned rect is in the layer space defined by 'mapping', so it directly represents
     *  the size and location of the SkDevice created to rasterize the content prior to invoking the
     *  image filter (assuming its CTM and basis matrix are configured to match 'mapping').
     *
     *  While this operation transforms an device-space output bounds to a layer-space input bounds,
     *  it is not necessarily the inverse of getOutputBounds(). For instance, a blur needs to have
     *  an outset margin when reading pixels at the edge (to satisfy its kernel), thus it expands
     *  its required input rect to include every pixel that contributes to the desired output rect.

     *  @param mapping       The coordinate space mapping that defines both the transformation
     *                       between local and layer, and layer to root device space, that will be
     *                       used when the filter is later invoked.
     *  @param desiredOutput The desired output boundary that needs to be covered by the filter's
     *                       output (assuming that the filter is then invoked with a suitable input)
     *  @param knownContentBounds
     *                       Optional, the known layer-space bounds of the non-transparent content
     *                       that would be rasterized in the source input image. Assumes unbounded
     *                       content when not provided.
     *
     * @return The layer-space bounding box to use for an SkDevice when drawing the source image.
     */
    skif::LayerSpace<SkIRect> getInputBounds(const skif::Mapping &mapping,
        const skif::DeviceSpace<SkIRect> &desiredOutput,
        std::optional<skif::ParameterSpace<SkRect>> knownContentBounds) const;

    /* *
     * Calculate the device-space bounds of the output of this filter DAG, if it were to process
     * an image layer covering the 'contentBounds'. The 'mapping' defines how the content will be
     * transformed to layer space when it is drawn, and how the output filter image is then
     * transformed to the final device space (i.e. it specifies the mapping between the root device
     * space and the parameter space of the initially provided content).
     *
     * While this operation transforms a parameter-space input bounds to an device-space output
     * bounds, it is not necessarily the inverse of getInputBounds(). For instance, a blur needs to
     * have an outset margin when reading pixels at the edge (to satisfy its kernel), so it will
     * generate a result larger than its input (so that the blur is visible) and, thus, expands its
     * output to include every pixel that it will touch.
     *
     * If the returned optional does not have a value, the caller should interpret this to mean
     * that the output of the image filter will fill the entirety of whatever clipped device it's
     * drawn into.
     *
     * @param mapping       The coordinate space mapping that defines both the transformation
     * between local and layer, and layer to root device space, that will be
     * used when the filter is later invoked.
     * @param contentBounds The local-space bounds of the non-transparent content that would be
     * drawn into the source image prior to filtering with this DAG,  i.e.
     * the same as 'knownContentBounds' in getInputBounds().
     *
     * @return The root device-space bounding box of the filtered image, were it applied to
     * content contained by 'contentBounds' and then drawn with 'mapping' to the root
     * device (w/o any additional clipping).
     */
    std::optional<skif::DeviceSpace<SkIRect>> getOutputBounds(const skif::Mapping &mapping,
        const skif::ParameterSpace<SkRect> &contentBounds) const;

    // Returns true if this image filter graph transforms a source transparent black pixel to a
    // color other than transparent black.
    bool affectsTransparentBlack() const;

    // Returns true if this image filter graph references the Context's source image.
    bool usesSource() const
    {
        return fUsesSrcInput;
    }

    /* *
     * This call returns the maximum "kind" of CTM for a filter and all of its (non-null) inputs.
     */
    using MatrixCapability = skif::MatrixCapability;
    MatrixCapability getCTMCapability() const;

    uint32_t uniqueID() const
    {
        return fUniqueID;
    }

    static SkFlattenable::Type GetFlattenableType()
    {
        return kSkImageFilter_Type;
    }

    SkFlattenable::Type getFlattenableType() const override
    {
        return kSkImageFilter_Type;
    }

    // TODO: CreateProcs for now-removed image filter subclasses need to hook into
    // SK_IMAGEFILTER_UNFLATTEN_COMMON, so this temporarily exposes it for the case where there's a
    // single input filter, and can be removed when the legacy CreateProcs are deleted.
    static std::pair<sk_sp<SkImageFilter>, std::optional<SkRect>> Unflatten(SkReadBuffer &buffer);

protected:
    class Common {
    public:
        /* *
         * Attempt to unflatten the expected number of input filters.
         * If any number of input filters is valid, pass -1.
         * If this fails (i.e. corrupt buffer or contents) then return false and common will
         * be left uninitialized.
         * If this returns true, then inputCount() is the number of found input filters, each
         * of which may be NULL or a valid imagefilter.
         */
        bool unflatten(SkReadBuffer &, int expectedInputs);

        std::optional<SkRect> cropRect() const
        {
            return fCropRect;
        }

        int inputCount() const
        {
            return fInputs.size();
        }
        sk_sp<SkImageFilter> *inputs()
        {
            return fInputs.begin();
        }

        sk_sp<SkImageFilter> getInput(int index)
        {
            return fInputs[index];
        }

    private:
        // Old SKPs (version less than kRemoveDeprecatedCropRect may have this set).
        std::optional<SkRect> fCropRect;

        // most filters accept at most 2 input-filters
        skia_private::STArray<2, sk_sp<SkImageFilter>, true> fInputs;
    };

    SkImageFilter_Base(sk_sp<SkImageFilter> const * inputs, int inputCount, std::optional<bool> usesSrc = {});

    ~SkImageFilter_Base() override;

    void flatten(SkWriteBuffer &) const override;

    // Helper function to calculate the required input/output of a specific child filter,
    // automatically handling if the child filter is null.
    skif::LayerSpace<SkIRect> getChildInputLayerBounds(int index, const skif::Mapping &mapping,
        const skif::LayerSpace<SkIRect> &desiredOutput, std::optional<skif::LayerSpace<SkIRect>> contentBounds) const;
    std::optional<skif::LayerSpace<SkIRect>> getChildOutputLayerBounds(int index, const skif::Mapping &mapping,
        std::optional<skif::LayerSpace<SkIRect>> contentBounds) const;

    // Helper function for recursing through the filter DAG. It automatically evaluates the input
    // image filter at 'index' using the given context. If the input image filter is null, it
    // returns the context's dynamic source image.
    //
    // When an image filter requires a different output than what is requested in it's own Context
    // passed to onFilterImage(), it should explicitly pass in an updated Context via
    // `withNewDesiredOutput`.
    skif::FilterResult getChildOutput(int index, const skif::Context &ctx) const;

private:
    friend class SkImageFilter;
    // For PurgeCache()
    friend class SkGraphics;

    static void PurgeCache();

    // Configuration points for the filter implementation, marked private since they should not
    // need to be invoked by the subclasses. These refer to the node's specific behavior and are
    // not responsible for aggregating the behavior of the entire filter DAG.

    /* *
     * Return true (and returns a ref'd colorfilter) if this node in the DAG is just a colorfilter
     * w/o cropping constraints.
     */
    virtual bool onIsColorFilterNode(SkColorFilter ** /* filterPtr */) const
    {
        return false;
    }

    /* *
     * Return the most complex matrix type this filter can support (mapping from its parameter
     * space to a layer space). If this returns anything less than kComplex, the filter only needs
     * to worry about mapping from parameter to layer using a matrix that is constrained in that
     * way (eg, scale+translate).
     */
    virtual MatrixCapability onGetCTMCapability() const
    {
        return MatrixCapability::kScaleTranslate;
    }

    /* *
     * Return true if this filter would transform transparent black pixels to a color other than
     * transparent black. When false, optimizations can be taken to discard regions known to be
     * transparent black and thus process fewer pixels.
     */
    virtual bool onAffectsTransparentBlack() const
    {
        return false;
    }

    /* *
     * Return true if `affectsTransparentBlack()` should only be based on
     * `onAffectsTransparentBlack()` and ignore the transparency behavior of child input filters.
     */
    virtual bool ignoreInputsAffectsTransparentBlack() const
    {
        return false;
    }

    /* *
     * This is the virtual which should be overridden by the derived class to perform image
     * filtering. Subclasses are responsible for recursing to their input filters, although the
     * filterInput() function is provided to handle all necessary details of this.
     *
     * If the image cannot be created (either because of an error or if the result would be empty
     * because it was clipped out), this should return a filtered Image with a null SkSpecialImage.
     * In these situations, callers that do not affect transparent black can end early, since the
     * "transparent" implicit image would be unchanged. Callers that affect transparent black need
     * to safely handle these null and empty images and return an image filling the context's clip
     * bounds as if its input filtered image were transparent black.
     */
    virtual skif::FilterResult onFilterImage(const skif::Context &context) const = 0;

    /* *
     * Calculates the necessary input layer size in order for the final output of the filter to
     * cover the desired output bounds. The provided 'desiredOutput' represents the requested
     * input bounds for this node's parent filter node, i.e. this function answers "what does this
     * node require for input in order to satisfy (as its own output), the input needs of its
     * parent?".
     *
     * 'contentBounds' represents the bounds of the non-transparent content that will form the
     * source image when the filter graph is invoked. If it's not instantiated, implementations
     * should treat the content as extending infinitely. However, since the output is known and
     * bounded, implementations should still be able to determine a finite input bounds under these
     * circumstances.
     *
     * Unlike the public getInputBounds(), all internal bounds calculations are done in the shared
     * layer space defined by 'mapping'.
     */
    virtual skif::LayerSpace<SkIRect> onGetInputLayerBounds(const skif::Mapping &mapping,
        const skif::LayerSpace<SkIRect> &desiredOutput,
        std::optional<skif::LayerSpace<SkIRect>> contentBounds) const = 0;

    /* *
     * Calculates the output bounds that this filter node would touch when processing an input
     * sized to 'contentBounds'. This function is responsible for recursing to its child image
     * filters and accounting for what they output. It is up to the filter to determine how to
     * aggregate the outputs of its children.
     *
     * 'contentBounds' represents the bounds of the non-transparent content that will form the
     * source image when the filter graph is invoked. If it's not instantiated, implementations
     * should treat the content as extending infinitely. However, since the output is known and
     * bounded, implementations should still be able to determine a finite input bounds under these
     * circumstances.
     *
     * If the non-transparent output extends infinitely, subclasses should return an uninstantiated
     * optional. Implementations must also be able to handle when their children return such
     * unbounded "outputs" and react accordingly.
     *
     * Unlike the public getOutputBounds(), all internal bounds calculations are done in the
     * shared layer space defined by 'mapping'.
     */
    // TODO (michaelludwig) - When layerMatrix = I, this function could be used to implement
    // onComputeFastBounds() instead of making filters implement the essentially the same calcs x2
    virtual std::optional<skif::LayerSpace<SkIRect>> onGetOutputLayerBounds(const skif::Mapping &mapping,
        std::optional<skif::LayerSpace<SkIRect>> contentBounds) const = 0;

    skia_private::AutoSTArray<2, sk_sp<SkImageFilter>> fInputs;

    bool fUsesSrcInput;
    uint32_t fUniqueID; // Globally unique

    using INHERITED = SkImageFilter;
};

static inline SkImageFilter_Base *as_IFB(SkImageFilter *filter)
{
    return static_cast<SkImageFilter_Base *>(filter);
}

static inline SkImageFilter_Base *as_IFB(const sk_sp<SkImageFilter> &filter)
{
    return static_cast<SkImageFilter_Base *>(filter.get());
}

static inline const SkImageFilter_Base *as_IFB(const SkImageFilter *filter)
{
    return static_cast<const SkImageFilter_Base *>(filter);
}

/* *
 * Helper to unflatten the common data, and return nullptr if we fail.
 */
#define SK_IMAGEFILTER_UNFLATTEN_COMMON(localVar, expectedCount) \
    Common localVar;                                             \
    do {                                                         \
        if (!localVar.unflatten(buffer, expectedCount)) {        \
            return nullptr;                                      \
        }                                                        \
    } while (0)


/* *
 * All image filter implementations defined for the include/effects/SkImageFilters.h factories
 * are entirely encapsulated within their own CPP files. SkFlattenable deserialization needs a hook
 * into these types, so their registration functions are exposed here.
 */
void SkRegisterBlendImageFilterFlattenable();
void SkRegisterBlurImageFilterFlattenable();
void SkRegisterColorFilterImageFilterFlattenable();
void SkRegisterComposeImageFilterFlattenable();
void SkRegisterCropImageFilterFlattenable();
void SkRegisterDisplacementMapImageFilterFlattenable();
void SkRegisterImageImageFilterFlattenable();
void SkRegisterLightingImageFilterFlattenables();
void SkRegisterMagnifierImageFilterFlattenable();
void SkRegisterMatrixConvolutionImageFilterFlattenable();
void SkRegisterMatrixTransformImageFilterFlattenable();
void SkRegisterMergeImageFilterFlattenable();
void SkRegisterMorphologyImageFilterFlattenables();
void SkRegisterPictureImageFilterFlattenable();
void SkRegisterRuntimeImageFilterFlattenable();
void SkRegisterShaderImageFilterFlattenable();

// TODO(michaelludwig): These filters no longer have dedicated implementations, so their
// SkFlattenable create procs only need to remain to support old SkPictures.
void SkRegisterLegacyDropShadowImageFilterFlattenable();

#endif // SkImageFilter_Base_DEFINED
